# -*- coding: utf-8 -*-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <style type="text/css">
        body, h1 { margin:0;padding:0; } /* needed for known issue with Dom.getXY() in safari for absolute elements in positioned containers */ 
        .space-line {clear:both;}
        textarea {border: 0px solid black; width:650px; height:275px;margin:0;padding:0;
                font-family:inherit; font-size:inherit;
                overflow:scroll;color:black;}
        .box    {color:black;
                width:650px;
                height:275px;
                overflow:scroll;
                font-size:inherit;
                background-color:#E9E9E9;
                margin:0; padding:0;
                }

        .username{font-family:inherit; font-size:inherit;background-color:lightgrey;}
        .boxh    {background-color:lightgrey;
                width:650px;
                font-size:10pt;
                font-family:monospace;}
    </style>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>${username}@${room.name} - YTalk online</title>

    <script type="text/javascript" src="http://comet.${TOPDOMAIN}/_/comet.js"></script> 
    <script type="text/javascript" src="/_/comet-helper.js"></script> 

</head>
<body class="yui-skin-sam">
    <div id="slot0" class="boxh">
        <div class="username">---------- me --------</div>
        <textarea id="mytextarea" onkeypress="myslot_onchange()"></textarea>
    </div>
    <script>
        function reversed(s) {
            return s.split("").reverse().join("");;
        }
        document.getElementById('slot0').getElementsByTagName('textarea')[0].focus();
        
        document.mytextarea_lastvalue = '';
        document.timer = '';
        document.timer_counter = 0;
        document.xhr = null;
        
        function myslot_onchange(){
            if(!document.timer){
                document.timer = window.setTimeout("timer()", document.delay_ms);
            }
        }
        function timer(){
            var ta = document.getElementById('mytextarea');
            var val = ta.value.replace(/\r/g, ' ');
            
            document.timer = null;
            if(val != document.mytextarea_lastvalue){
                var diff;
                if(document.timer_counter % document.full_resend_counter == 0){
                    diff = diff_create(document.mytextarea_lastvalue, val, true);
                }else{
                    diff = diff_create(document.mytextarea_lastvalue, val, false);
                }
                if(!document.xhr){
                    document.timer_counter = document.timer_counter + 1;
                    document.mytextarea_lastvalue = val;
                    
                    send_diff(diff);
                }
                
                document.timer = window.setTimeout("timer()", document.delay_ms);
            }
        }
        function send_diff(diff){
            var form = document.getElementById('theForm');
            var data  = document.getElementById('form_data');
            var index = document.getElementById('form_index');
            
            var v = escape('data ${username} ' + diff.index + ' ' + reversed(diff.data) + '|').replace(/\+/g, '%2B');
            
            function cb(xhr){
                if(xhr.readyState != 4)
                    return;
                if(xhr.status != 200){
                    channel_id = '';
                    update_uri();
                }
                document.xhr = null;
            }
            ajax_crossdomain('http://comet.${TOPDOMAIN}/_/crossajax.html?a=a', 'POST', '/comet/' + channel_id + '/event', cb,
                    'data=' + v );
            kill_load_bar();
            document.xhr = true;
        }
    </script>
    <div style="display:none;">
        <form action="." method="post" onsubmit="return false" id="theForm" autocomplete="off">
            <textarea id="form_data"  name="data"></textarea>
            <input type="text" id="form_index" name="index" value="" />
        <!--             <input type="text" id="form_data" name="data" value="" />   -->
        </form>
    </div>
    
    
    % for i in range(1,5):
    <div id="slot${i}" class="boxh">
        <div class="username">---------- (nobody connected) --------</div><div class="space-line"></div>
        <pre class="box"></pre>
    </div>
    % endfor


    <script>
        document.uid_to_slotno = {};
        document.slots_used = 1;
        document.delay_ms = 100;
        document.full_resend_counter = 20;
        document.maxid = 0;
        
        function set_visible_slots(i){
            var s2, s3, s4, j, height;
            if(i < 3){ // = 585 or 584 px
                s2 = 'none'; s3='none'; s4='none';
                height = "275px";
            }else if(i==3){
                s2 = 'block'; s3='none'; s4='none';
                height = "178px";
            }else if(i==4){
                s2 = 'block'; s3='block'; s4='none';
                height = "129px";
            }else{
                s2 = 'block'; s3='block'; s4='block';
                height = "100px";
            }
            document.getElementById('slot2').style.display = s2;
            document.getElementById('slot3').style.display = s3;
            document.getElementById('slot4').style.display = s4;
            document.getElementById('slot0').getElementsByTagName('textarea')[0].style.height = height;
            for(j=1; j<5; j++){
                document.getElementById('slot' + j).getElementsByTagName('pre')[0].style.height = height;
            }
        }
        set_visible_slots(2);
        for(var i=1; i<5; i++){
            document.getElementById('slot' + i).getElementsByTagName('pre')[0].v = '';
        }
        function clear_slot(i){
            document.getElementById('slot' + i).getElementsByTagName('div')[0].innerHTML = '---------- (nobody connected) --------';
            set_slot_text(i,'');
        }
        function move_slot(i, j){
            var v   = document.getElementById('slot' + i).getElementsByTagName('pre')[0].v;
            var val = document.getElementById('slot' + i).getElementsByTagName('pre')[0].innerHTML;
            var nam = document.getElementById('slot' + i).getElementsByTagName('div')[0].innerHTML;
            document.getElementById('slot' + j).getElementsByTagName('pre')[0].v         = v;
            document.getElementById('slot' + j).getElementsByTagName('pre')[0].innerHTML = val;
            document.getElementById('slot' + j).getElementsByTagName('div')[0].innerHTML  = nam;
        }
        function set_slot_name(i, name){
            document.getElementById('slot' + i).getElementsByTagName('div')[0].innerHTML = '---------- ' + name + ' --------';
        }
        var fromTo = new Array( '&amp;','&', 
                                '&lt;', '<', 
                                '&gt;', '>', 
                                '&quot;', "\"");
        function replaceExtChars(text) {
            for (i=0; i < fromTo.length; i=i+2)
                text = text.replace(eval('/'+fromTo[i+1]+'/g'), fromTo[i]);
            text = text.replace(/\n/g, '<br class="space-line" />');
            text = text.replace(/\r/g, '');
            return (text);
        }
        function set_slot_text(i, text){
            var ta = document.getElementById('slot' + i).getElementsByTagName('pre')[0];
            ta.v = text;
            ta.innerHTML = replaceExtChars(text);
            ta.scrollTop = ta.scrollHeight;
        }

        function diff_create(o, n, force){
            var i, s;
            var min;
            if(o.length < n.length){
                min = o.length;
            }else if(o.length > n.length){
                min = n.length;
            }else{ // ==
                if(o == n){ // no change
                    var ret={'index':0, 'data':''};
                    return ret;
                }
                min = n.length;
            }
            s = min - 1300;
            if(s < 0)
                s = 0;
            var j = min;
            for(i=s; i<min; i++){
                if((o.substr(i,1) != n.substr(i,1) ) || (force==true)){
                    j = i;
                    break;
                }
            }
            var ret={}
            if(force)
                ret.index = -1;
            else
                ret.index = o.length - j;
            
            ret.data  = n.substr(j);
            //alert(o + ' ' + n + ' ' +o.length + ' ' + s + '<'+ min + ' index:' + ret.index + ' leng:' + ret.data.length + ' data:' +ret.data)
            return ret;
        }
        function diff_apply(old, diff){
            if(diff.index == -1 || diff.index == '-1'){
                return(diff.data);
            }
            return old.substr(0, old.length - diff.index) + diff.data;
        }
        //diff_apply("marek ma kota", diff_create("marek ma kota", "marek ma kotka i dwa") )
        function set_slot_text_apply_diff(i, diff){
            var old = document.getElementById('slot' + i).getElementsByTagName('pre')[0].v;
            set_slot_text(i, diff_apply(old, diff));
        }

        
        function new_slot(name, uid){
            if(document.uid_to_slotno[uid])
                return;
            if(document.slots_used == 5){
                alert("can't add more users to the chat! " + name );
                return;
            }
            var slot = document.slots_used;
            document.slots_used = document.slots_used + 1;
            document.uid_to_slotno[uid] = slot;
            clear_slot(slot);
            set_slot_name(slot, name);
            set_visible_slots(document.slots_used);
        }
        function del_slot(uid){
            var slot = document.uid_to_slotno[uid];
            if(!slot){
                //alert("bad uid " + uid);
                return;
            }
            var i;
            for(i=slot; i < (document.slots_used-1); i++){
                move_slot(i+1, i);
            }
            for(var u in document.uid_to_slotno){
                var s = document.uid_to_slotno[u];
                if(s > slot)
                    document.uid_to_slotno[u] -= 1;
            }
            clear_slot(document.slots_used - 1);
            document.slots_used = document.slots_used - 1;
            document.uid_to_slotno[uid] = null;
            set_visible_slots(document.slots_used);
        }
        function slots_refresh(uids){
            var i, j;
            for(var uid in document.uid_to_slotno){
                if(!uids[uid]){
                    del_slot(uid);
                }
            }
            for(var uid in uids){
                if(!document.uid_to_slotno[uid] && uid != '${username}'){
                    new_slot(uids[uid], uid);
                }
            }
        }
        //update_slot(4, {index:0, data:"alam4"})
        function update_slot(uid, diff){
            var slot = document.uid_to_slotno[uid];
            if(!slot){
                //alert("bad uid " + uid);
                return;
            }
            set_slot_text_apply_diff(slot, diff);
        }
        function update_delay_ms(t, r){
            document.delay_ms = t;
            document.full_resend_counter = r;
        }

        






        /* yep. we have to change document.domain */
        document.domain = extract_xss_domain(document.domain);

        function user_comet_callback(id, author, data) {
            var a        = data.split(' ');
            var command  = a[0];
            var parameter= a[1];
            /* user event */
            if(parameter == '${username}')
                return;
            if(command == 'login' && author == 'admin'){
                new_slot(parameter, parameter);
                return;
            }
            if(command == 'logout' && author == 'admin'){
                del_slot(parameter);
                return;
            }
            if(command == 'data' && author == 'guest'){
                var data = a.slice(3).join(' ');
                data = data.substr(0, data.length-1);
                var diff = {}
                diff.index = Number(a[2]);
                diff.data = reversed(data)
                update_slot(parameter, diff);
            }
        }
        function user_channel_callback(channel_id_changed, json){
            var uids={};
            for(var i=0; i < json.logged_users.length; i++){
                var uname = json.logged_users[i];
                if(uname != '${username}')
                    uids[uname]=uname;
            }
            slots_refresh(uids);
        }
        generate_channel_id_updater('http://comet.${TOPDOMAIN}/_/iframe.html?a=a', '${username}', user_comet_callback, user_channel_callback);


    </script>

<br />
<br />
<br />
<br />
You should know:
<ul>
    <li>only <i>tail</i> of your input box is transmitted to other peers (last 200 bytes). editions in previous data will not be sent</li>
    <li>this website is hosted on <a href="http://code.google.com/appengine/">Google App Engine</a></li>
    <li>it's using external <a href="http://movq.net">comet service</a></li>
    <li>the sources are <a href="http://code.google.com/p/gae-services-examples/source/browse">availbe public</a></li>
    <li>the users of this chat are not authorized! one user can easilly send message as another one.</li>
</ul>
</body>
</html>


